﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using SalesSys.Comm.DAL;

namespace SalesSys.Comm.Utility
{
    public static class CollectionsExt
    {

        /// <summary>
       /// 创建一个排序用的键值对
       /// </summary>
       /// <param name="colName"></param>
       /// <returns></returns>
        public static KeyValuePair<string, bool> ToSortCondition(this string colName,bool isDesc=false)
       {  
            return new KeyValuePair<string, bool>(colName, isDesc);
       }
        public static bool CheckOrderBy(this IEnumerable<KeyValuePair<string, bool>> orderBy, params string[] colNames)
        { 
         if (orderBy==null||orderBy.Count()<1)
           {
               return false;
           }
           else
           {
              return orderBy.All(p => colNames.Any(x => x == p.Key));
           }
        }
      
        #region 公共方法

        /// <summary>
        /// 添加一个对象到列表，直接调用对象toString
        /// </summary>
        /// <param name="nv"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
       public static void Add(this NameValueCollection nv,string name, object value)
       {
           if (value!=null)
           {
               nv.Add(name, value.ToString());
           }
           nv.Add(name, null);
       }
      public static void AddRange<T>(this ICollection<T> source,IEnumerable<T> collection)
       {
           if (source==null||collection==null)
           {
               return;
           }
           foreach (var item in collection)
           {
               source.Add(item);
           }
       }
      public static KeyValuePair<string, string>[] ToKeyValueArray(this NameValueCollection nv)
      {
          var keys=nv.AllKeys;
          KeyValuePair<string,string>[] kvArray=new KeyValuePair<string,string>[keys.Length];
          for (int i = 0; i < keys.Length; i++)
          {
              kvArray[i] = new KeyValuePair<string, string>(keys[i], nv[keys[i]]);
          }
          return kvArray;
      }

        #region IEnumerable的扩展

        ///// <summary>
        /////     将集合展开并分别转换成字符串，再以指定的分隔符衔接，拼成一个字符串返回
        ///// </summary>
        ///// <param name="collection"> 要处理的集合 </param>
        ///// <param name="separator"> 分隔符 </param>
        ///// <returns> 拼接后的字符串 </returns>
        //public static string ExpandAndToString<T>(this IEnumerable<T> collection, string separator)
        //{
        //    List<T> source = collection as List<T> ?? collection.ToList();
        //    if (source.IsEmpty())
        //    {
        //        return null;
        //    }
        //    string result = source.Cast<object>().Aggregate<object, string>(null, (current, o) => current + string.Format("{0}{1}", o, separator));
        //    return result.Substring(0, result.Length - separator.Length);
        //}

        /// <summary>
        ///     集合是否为空
        /// </summary>
        /// <param name="collection"> 要处理的集合 </param>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <returns> 为空返回True，不为空返回False </returns>
        public static bool IsEmpty<T>(this IEnumerable<T> collection)
        {
            return !collection.Any();
        }

        /// <summary>
        ///     根据第三方条件是否为真来决定是否执行指定条件的查询
        /// </summary>
        /// <param name="source"> 要查询的源 </param>
        /// <param name="predicate"> 查询条件 </param>
        /// <param name="condition"> 第三方条件 </param>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <returns> 查询的结果 </returns>
        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool condition)
        {
            return condition ? source.Where(predicate) : source;
        }

        /// <summary>
        ///     根据指定条件返回集合中不重复的元素
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <typeparam name="TKey">动态筛选条件类型</typeparam>
        /// <param name="source">要操作的源</param>
        /// <param name="keySelector">重复数据筛选条件</param>
        /// <returns>不重复元素的集合</returns>
        public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
        {
            return source.GroupBy(keySelector).Select(group => group.First());
        }

        #endregion

        #region IQueryable的扩展

        /// <summary>
        ///     根据第三方条件是否为真来决定是否执行指定条件的查询
        /// </summary>
        /// <param name="source"> 要查询的源 </param>
        /// <param name="predicate"> 查询条件 </param>
        /// <param name="condition"> 第三方条件 </param>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <returns> 查询的结果 </returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, bool condition)
        {
           
            return condition ? source.Where(predicate) : source;
        }

        /// <summary>
        ///     把IQueryable[T]集合按指定属性与排序方式进行排序
        /// </summary>
        /// <param name="source">要排序的数据集</param>
        /// <param name="propertyName">排序属性名</param>
        /// <param name="sortDirection">排序方向</param>
        /// <typeparam name="T">动态类型</typeparam>
        /// <returns>排序后的数据集</returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName,bool isDescSort=false)
        {

            return QueryableHelper<T>.OrderBy(source, propertyName, isDescSort);
        }

        /// <summary>
        ///     把IQueryable[T]集合按指定属性排序条件进行排序
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="source">要排序的数据集</param>
        /// <param name="sortCondition">列表属性排序条件</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, KeyValuePair<string, bool> sortCondition)
        {
            return source.OrderBy(sortCondition.Key, sortCondition.Value);
        }
        public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool isDESC)
        {
            return isDESC ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
        }

        /// <summary>
        ///     把IOrderedQueryable[T]集合继续按指定属性排序方式进行排序
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="source">要排序的数据集</param>
        /// <param name="propertyName">排序属性名</param>
        /// <param name="sortDirection">排序方向</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string propertyName,
              bool isDescSort=false)
        {

            return QueryableHelper<T>.ThenBy(source, propertyName, isDescSort);
        }

        /// <summary>
        ///     把IOrderedQueryable[T]集合继续指定属性排序方式进行排序
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="source">要排序的数据集</param>
        /// <param name="sortCondition">列表属性排序条件</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, KeyValuePair<string, bool> sortCondition)
        {
            return source.ThenBy(sortCondition.Key, sortCondition.Value);
        }
        public static IQueryable<TEntity> Paging<TEntity>(this IQueryable<TEntity> source, int pageIndex, int pageSize,
            KeyValuePair<string, bool>[] sortConditions) where TEntity : IDbEntity
        {
            if (sortConditions != null && sortConditions.Length > 0)
            {
                IOrderedQueryable<TEntity> orderSource = null;
                for (int i = 0; i < sortConditions.Length; i++)
                {
                    var sortCondition = sortConditions[i];
                    orderSource = i == 0
                        ? source.OrderBy(sortCondition.Key, sortCondition.Value)
                        : orderSource.ThenBy(sortCondition.Key, sortCondition.Value);
                }
                source = orderSource;
            }
            return source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
                : Enumerable.Empty<TEntity>().AsQueryable();
        }
        public static IQueryable<TEntity> Paging<TEntity>(this IOrderedQueryable<TEntity> source, int pageIndex, int pageSize) 
        {
            return source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
                : Enumerable.Empty<TEntity>().AsQueryable();
        }
        public static IQueryable<TEntity> Paging<TEntity>(this IOrderedEnumerable<TEntity> source, int pageIndex, int pageSize)
        {
            return source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize).AsQueryable()
                : Enumerable.Empty<TEntity>().AsQueryable();
        }
        /// <summary>
        ///     从指定IQueryable[T]集合 中查询指定分页条件的子数据集
        /// </summary>
        /// <typeparam name="TEntity">动态实体类型</typeparam>
        /// <typeparam name="TKey">实体主键类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageIndex">页码，从1开始</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="total">输出符合条件的总记录数</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Query<TEntity>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize,
            out int total, KeyValuePair<string, bool>[] sortConditions) where TEntity : IDbEntity 
        {
            if (predicate!=null)
            {
                 source = source.Where(predicate);
            }
           
            total = source.Count();
            if (sortConditions == null || sortConditions.Length == 0)
            {
                throw new ArgumentException("Linq To EF 分页必须先进行排序", "sortConditions");
            }
            else
            {              
                IOrderedQueryable<TEntity> orderSource = null;
                for (int i = 0; i < sortConditions.Length; i++)
                {
                    var sortCondition = sortConditions[i];
                    orderSource = i == 0
                        ? source.OrderBy(sortCondition.Key, sortCondition.Value)
                        : orderSource.ThenBy(sortCondition.Key, sortCondition.Value);
                } 
                source = orderSource;
            }
            return source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
                : Enumerable.Empty<TEntity>().AsQueryable();
        }

        #endregion

        #endregion

        #region 辅助操作类

        private static class QueryableHelper<T>
        {
            // ReSharper disable StaticFieldInGenericType
            private static readonly ConcurrentDictionary<string, LambdaExpression> Cache = new ConcurrentDictionary<string, LambdaExpression>();

            internal static IOrderedQueryable<T> OrderBy(IQueryable<T> source, string propertyName, bool isDescSort=false)
            {
                dynamic keySelector = GetLambdaExpression(propertyName);
                return isDescSort  ? Queryable.OrderByDescending(source, keySelector): Queryable.OrderBy(source, keySelector);
            }

            internal static IOrderedQueryable<T> ThenBy(IOrderedQueryable<T> source, string propertyName, bool isDescSort = false)
            {
                dynamic keySelector = GetLambdaExpression(propertyName);
                return isDescSort ? Queryable.OrderByDescending(source, keySelector) : Queryable.OrderBy(source, keySelector);
            }

            private static LambdaExpression GetLambdaExpression(string propertyName)
            {
                if (Cache.ContainsKey(propertyName))
                {
                    return Cache[propertyName];
                }
                ParameterExpression param = Expression.Parameter(typeof(T));
                MemberExpression body = Expression.Property(param, propertyName);
                LambdaExpression keySelector = Expression.Lambda(body, param);
                Cache[propertyName] = keySelector;
                return keySelector;
            }
        }

        #endregion
    }
}
